package com.wmh.baseservice.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wmh.baseservice.admin.cache.permission.MenuRepository;
import com.wmh.baseservice.admin.entity.AdMenu;
import com.wmh.baseservice.admin.entity.AdRole;
import com.wmh.baseservice.admin.mapper.AdMenuMapper;
import com.wmh.baseservice.admin.pojo.vo.menu.*;
import com.wmh.baseservice.admin.service.AdMenuService;
import com.wmh.baseservice.admin.service.AdRoleMenuService;
import com.wmh.baseservice.admin.service.AdRoleService;
import com.wmh.baseservice.admin.tools.enums.AdMenuFlag;
import com.wmh.baseservice.admin.tools.enums.AdRoleStatus;
import com.wmh.baseservice.common.ex.ServiceException;
import com.wmh.baseservice.common.mybatisplus.methods.CommonServiceImpl;
import com.wmh.baseservice.common.mybatisplus.structure.LambdaQueryWrapperX;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author mmx generator
 * @since 2021-02-03
 */
@Service
@Slf4j
public class AdMenuServiceImpl extends CommonServiceImpl<AdMenuMapper, AdMenu> implements AdMenuService {
    @Resource
    private AdMenuMapper adMenuMapper;

    @Resource
    private MenuRepository menuRepository;

    @Resource
    private AdRoleService adRoleService;

    @Resource
    private AdRoleMenuService adRoleMenuService;



    @Resource
    private HttpServletRequest request;

    @PostConstruct
    private void initCache() {
        log.info("初始化菜单缓存");
        menuRepository.clear();
        List<AdMenu> menus = baseMapper.selectList(null);
        menuRepository.setMenu(menus);
        log.info("缓存菜单数量 : [{}]", menus.size());
    }


    @Override
    public void addV2(AddV2ReqVo reqVo) {
        // 校验必填参数
        checkRequiredParam(reqVo);
        // 校验字段不可重复性
        checkFieldsUniqueness(null, reqVo);
        AdMenu entity = AddAndUpdateReqFields.buildEntity(null, reqVo);
        baseMapper.insert(entity);
        menuRepository.setMenu(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteV2(Long id) {
        checkIsExist(id);
        List<AdMenu> menus = baseMapper.selectList(new LambdaQueryWrapperX<AdMenu>()
                .eq(AdMenu::getParentId, id));
        if (CollUtil.isNotEmpty(menus)){
            throw new ServiceException("请先删除子级菜单");
        }
        baseMapper.delete(new LambdaQueryWrapper<AdMenu>()
                .eq(AdMenu::getId, id));
        menuRepository.delMenu(id);
        adRoleMenuService.delete(id);
    }

    @Override
    public void updateV2(UpdateV2ReqVO reqVO) {
        checkIsExist(reqVO.getId());
        // 校验必填参数
        checkRequiredParam(reqVO);
        // 校验字段不可重复性
        checkFieldsUniqueness(reqVO.getId(), reqVO);
        AdMenu entity = AddAndUpdateReqFields.buildEntity(reqVO.getId(), reqVO);
        baseMapper.updateById(entity);
        menuRepository.setMenu(entity);
    }

    private void checkFieldsUniqueness(Long id, AddAndUpdateReqFields req) {
        AdMenu menu = baseMapper.selectOne(new LambdaQueryWrapperX<AdMenu>()
                .neIfPresent(AdMenu::getId, id)
                .eq(AdMenu::getParentId, req.getParentId())
                .eq(AdMenu::getMenuName, req.getName()));
        if (menu != null) {
            throw new ServiceException("名称重复");
        }
        if (AdMenuFlag.MENU.getValue().equals(req.getFlag())) {
            if (StrUtil.isNotBlank(req.getComponent())) {
                menu = baseMapper.selectOne(new LambdaQueryWrapperX<AdMenu>()
                        .neIfPresent(AdMenu::getId, id)
                        .eq(AdMenu::getComponent, req.getComponent()));
                if (menu != null) {
                    throw new ServiceException("存在相同的组件路径");
                }
            }
            if (StrUtil.isNotBlank(req.getRequestUrl())) {
                menu = baseMapper.selectOne(new LambdaQueryWrapperX<AdMenu>()
                        .neIfPresent(AdMenu::getId, id)
                        .eq(AdMenu::getRequestUrl, req.getRequestUrl()));
                if (menu != null) {
                    throw new ServiceException("存在相同的请求路径");
                }
            }
        } else if (AdMenuFlag.INTERFACE.getValue().equals(req.getFlag())) {
            menu = baseMapper.selectOne(new LambdaQueryWrapperX<AdMenu>()
                    .neIfPresent(AdMenu::getId, id)
                    .eq(AdMenu::getPermissions, req.getPermissions()));
            if (menu != null) {
                throw new ServiceException("存在相同的权限代码");
            }
        } else {
            throw new ServiceException("不支持的菜单类型");
        }

    }


    private void checkRequiredParam(AddAndUpdateReqFields req) {
        if (AdMenuFlag.MENU.getValue().equals(req.getFlag())) {
            if (req.getParentId() != -1) {
                if (StrUtil.isBlank(req.getComponent())) {
                    throw new ServiceException("未提交组件路径");
                }
                if (StrUtil.isBlank(req.getRequestUrl())) {
                    throw new ServiceException("未提交访问路径");
                }
            }
        } else if (AdMenuFlag.INTERFACE.getValue().equals(req.getFlag())) {
            if (StrUtil.isBlank(req.getPermissions())) {
                throw new ServiceException("未提交权限代码");
            }
        } else {
            throw new ServiceException("不支持的菜单类型");
        }
    }

    private void checkIsExist(Long id) {
        AdMenu menu = baseMapper.selectById(id);
        if (menu == null) {
            throw new ServiceException("该数据已不存在");
        }
    }

    @Override
    public List<AdMenu> getListByRoleIds(Integer flag, Long... roleIds) {
        return adMenuMapper.selectPermissionsByRoleIds(flag, roleIds);
    }

    @Override
    public Map<Integer, List<AdMenu>> getMenuMapping(Long roleId) {
        List<AdMenu> list = getListByRoleIds(null, roleId);
        Map<Integer, List<AdMenu>> map = new HashMap<>();
        map.put(AdMenuFlag.INTERFACE.getValue(), new ArrayList<AdMenu>());
        map.put(AdMenuFlag.MENU.getValue(), new ArrayList<AdMenu>());
        for (AdMenu menu : list) {
            List<AdMenu> adMenus = map.get(menu.getFlag());
            adMenus.add(menu);
        }
        return map;
    }

    @Override
    public List<AdMenu> subList(Long parentId) {
        return baseMapper.list(parentId);
    }

    @Override
    public List<MenuTreeVo> treeList() {
        // 获取所有菜单
        List<MenuTreeVo> menus = list().stream().map(MenuTreeVo::build).collect(Collectors.toList());
        // 主键映射 map 方便操作
        Map<Long, MenuTreeVo> idMap = menus.stream().collect(Collectors.toMap(MenuTreeVo::getId, t -> t));

        // 通过 parentId 索引 idMap 建立父子结构
        menus.forEach(item -> {
            // 根目录直接跳过
            if (item.getParentId() == -1) {
                return;
            }
            // 索引父
            MenuTreeVo parentMenu = idMap.get(item.getParentId());
            if (parentMenu != null) {
                List<MenuTreeVo> children = parentMenu.getChildren();
                if (children == null) {
                    children = new ArrayList<>();
                }
                children.add(item);
                parentMenu.setChildren(children);
            }
        });
        // 结构好后直接返回根目录
        return menus.stream().filter(i -> i.getParentId() == -1).collect(Collectors.toList());
    }

    @Override
    public List<AdMenu> getAdMenuByCache(Long adId, AdMenuFlag flag) {
        List<Long> roleIds = adRoleService.getAdRolesByCache(adId, AdRoleStatus.NORMAL)
                .stream().map(AdRole::getId).collect(Collectors.toList());
        List<AdMenu> r;
        // 超级管理员获取所有菜单
        if (adRoleService.isHasSupperAdmin(roleIds)) {
            r = menuRepository.all();
        } else {
            Set<Long> menuIds = new HashSet<>();
            roleIds.forEach(t -> {
                List<Long> ids = adRoleMenuService.getMenuIds(t);
                menuIds.addAll(ids);
            });
            r = menuRepository.list(new ArrayList<>(menuIds));
        }
        if (flag != null) {
            r = r.stream().filter(i -> flag.getValue().equals(i.getFlag()))
                    .collect(Collectors.toList());
        }
        return r;
    }


    private void checkAddOrUpdateMenuReqVOParam(AddOrUpdateMenuReqVO vo) {
        if (vo.getFlag().equals(AdMenuFlag.MENU.getValue())) {
            if (vo.getParentId() == null) {
                throw new ServiceException("您没有上传 [父级菜单编号]");
            }
            if (vo.getSort() == null || vo.getSort() < 0) {
                throw new ServiceException("请正确上传 [排序编号] 至少为 0");
            }

            if (vo.getParentId() != -1 && StringUtils.isAnyBlank(vo.getRequestUrl(), vo.getComponent())) {
                throw new ServiceException("表单存在内容为空的参数");
            }
        } else if (vo.getFlag().equals(AdMenuFlag.INTERFACE.getValue())) {
            if (StringUtils.isAnyBlank(vo.getPermissions())) {
                throw new ServiceException("表单存在内容为空的参数");
            }
        } else {
            throw new ServiceException("您上传了一个不支持的 [菜单类型]");
        }
    }
}
